package com.bryantkobe.sort;

import java.util.Arrays;

/**
 * @ClassName MergeSort
 * @Description 归并排序
 * @Author kobe
 * @Date 2021/4/27 20:07
 */
public class MergeSort {
    public static void main(String[] args) {
        //定义一个数组
        //int[] arr = {8,4,5,7,1,3,6,2};
        int[] arr = new int[100000];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int)(Math.random()*100000);
        }
        //归并排序需要一个额外的空间
        int[] temp = new int[arr.length];

        long startTime = System.currentTimeMillis();
        mergeSort(arr,0,arr.length - 1 ,temp);
        long endTime = System.currentTimeMillis();

        //归并算法执行时间:26
        System.out.println("归并算法执行时间:"+(endTime-startTime));

        //System.out.println("归并排序后："+ Arrays.toString(arr));
    }

    /**
     * 分+合方法
     * @param arr 数组
     * @param left 左边索引
     * @param right 右边索引
     * @param temp 额外数组
     */
    private static void mergeSort(int[] arr, int left, int right, int[] temp) {
        if (left < right){
            //中间索引
            int mid = (left + right) / 2;

            //向左递归进行分解
            mergeSort(arr,left,mid,temp);

            //向右递归进行分解
            mergeSort(arr,mid +1 ,right,temp);

            //合并
            merge(arr,left,mid,right,temp);
        }
    }

    /**
     * 合并方法
     * @param arr 排序的原始数组
     * @param left 左边有序序列的初始索引
     * @param mid 中间索引
     * @param right 右边索引
     * @param temp 额外数组（中转的数组）
     */
    private static void merge(int[] arr, int left, int mid, int right, int[] temp) {
        //初始化i，左边有序序列的初始索引
        int i = left;
        //初始化j，右边有序序列的初始索引
        int j = mid +1;
        //指向temp数组的当前索引
        int t = 0;

        /**
         * (一)
         * 先把左右两边（有序）的数据按照规则填充到temp数组
         * 直到左右两边的有序序列，有一边处理完毕为止
         */
        while (i <= mid && j <= right) {
            /*
             * 如果左边的有序序列的当前元素小于等于右边有序序列的当前元素
             * 即将左边的当前元素，填充到temp数组
             * 然后t++,i++
             * */
            if (arr[i] <= arr[j]) {
                temp[t] = arr[i];
                t += 1;
                i += 1;
            } else {
                /*
                 * 反之，将右边有序序列的当前元素填充到temp数组中
                 * */
                temp[t] = arr[j];
                t += 1;
                j += 1;
            }
        }

        /**
         * (二)
         * 把有剩余数据的一边数据依次全部填充到temp
         */
        while (i <= mid){
            /*
            * 左边的有序序列还有剩余元素，就全部填充到temp
            * */
            temp[t] = arr[i];
            t += 1;
            i += 1;
        }
        while (j <= right){
            /*
            * 右边的有序序列还有剩余元素，就全部填充到temp
            * */
            temp[t] = arr[j];
            t += 1;
            j += 1;
        }

        /**
         * (三)
         * 将temp数组的元素拷贝到arr
         * 注意：并不是每次都拷贝所有
         */
        t = 0;
        int tempLeft = left;
        /*
        * 第一次合并tempLeft = 0，right = 1 // tempLeft = 2,right = 3 //tl = 0,ri=3
        * 最后一次tempLeft = 0，right = 7
        * */
        //System.out.println("tempLeft:"+tempLeft+",right:"+right);
        while (tempLeft <= right){
            arr[tempLeft] = temp[t];
            t += 1;
            tempLeft += 1;
        }
    }
}
